﻿using System;

namespace Motar
{

    /// <summary>
    /// 
    /// </summary>
    public static class ApplicationActivator
    {

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <param name="action"></param>
        public static void Run<TBootstrapper>(Action<TBootstrapper> action) where TBootstrapper : IBootstrapper, new()
        {
            Run<TBootstrapper, bool>(bootstrapper =>
            {
                action.Invoke(bootstrapper);

                return true;
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TResult Run<TBootstrapper, TResult>(Func<TBootstrapper, TResult> action) where TBootstrapper : IBootstrapper, new()
        {
            using (TBootstrapper bootstrapper = Run<TBootstrapper>())
            {
                return action.Invoke(bootstrapper);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <param name="exceptionHandler"></param>
        /// <param name="action"></param>
        public static void Run<TBootstrapper>(Action<TBootstrapper, Exception> exceptionHandler, Action<TBootstrapper> action) where TBootstrapper : IBootstrapper, new()
        {
            Run<TBootstrapper, bool>(exceptionHandler, bootstrapper =>
            {
                action.Invoke(bootstrapper);

                return true;
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="exceptionHandler"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TResult Run<TBootstrapper, TResult>(Action<TBootstrapper, Exception> exceptionHandler, Func<TBootstrapper, TResult> action) where TBootstrapper : IBootstrapper, new()
        {
            using (TBootstrapper bootstrapper = Run<TBootstrapper>(exceptionHandler))
            {
                return action.Invoke(bootstrapper);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <param name="bootstrapperFactory"></param>
        /// <param name="action"></param>
        public static void Run<TBootstrapper>(Func<TBootstrapper> bootstrapperFactory, Action<TBootstrapper> action) where TBootstrapper : IBootstrapper
        {
            Run<TBootstrapper, bool>(bootstrapperFactory, bootstrapper =>
            {
                action.Invoke(bootstrapper);

                return true;
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="bootstrapperFactory"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TResult Run<TBootstrapper, TResult>(Func<TBootstrapper> bootstrapperFactory, Func<TBootstrapper, TResult> action) where TBootstrapper : IBootstrapper
        {
            using (TBootstrapper bootstrapper = Run<TBootstrapper>(bootstrapperFactory))
            {
                return action.Invoke(bootstrapper);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <param name="bootstrapperFactory"></param>
        /// <param name="exceptionHandler"></param>
        /// <param name="action"></param>
        public static void Run<TBootstrapper>(Func<TBootstrapper> bootstrapperFactory, Action<TBootstrapper, Exception> exceptionHandler, Action<TBootstrapper> action) where TBootstrapper : IBootstrapper
        {
            Run<TBootstrapper, bool>(bootstrapperFactory, exceptionHandler, bootstrapper =>
            {
                action.Invoke(bootstrapper);

                return true;
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="bootstrapperFactory"></param>
        /// <param name="exceptionHandler"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static TResult Run<TBootstrapper, TResult>(Func<TBootstrapper> bootstrapperFactory, Action<TBootstrapper, Exception> exceptionHandler, Func<TBootstrapper, TResult> action) where TBootstrapper : IBootstrapper
        {
            using (TBootstrapper bootstrapper = Run<TBootstrapper>(bootstrapperFactory, exceptionHandler))
            {
                return action.Invoke(bootstrapper);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <returns></returns>
        public static TBootstrapper Run<TBootstrapper>() where TBootstrapper : IBootstrapper, new()
        {
            return Run<TBootstrapper>(() => new TBootstrapper());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <param name="exceptionHandler"></param>
        /// <returns></returns>
        public static TBootstrapper Run<TBootstrapper>(Action<TBootstrapper, Exception> exceptionHandler) where TBootstrapper : IBootstrapper, new()
        {
            return Run<TBootstrapper>(() => new TBootstrapper(), exceptionHandler);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <param name="bootstrapperFactory"></param>
        /// <returns></returns>
        public static TBootstrapper Run<TBootstrapper>(Func<TBootstrapper> bootstrapperFactory) where TBootstrapper : IBootstrapper
        {
            return Run(bootstrapperFactory, (Action<TBootstrapper, Exception>)null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TBootstrapper"></typeparam>
        /// <param name="bootstrapperFactory"></param>
        /// <param name="exceptionHandler"></param>
        /// <returns></returns>
        public static TBootstrapper Run<TBootstrapper>(Func<TBootstrapper> bootstrapperFactory, Action<TBootstrapper, Exception> exceptionHandler) where TBootstrapper : IBootstrapper
        {
            TBootstrapper bootstrapper = default(TBootstrapper);

            try
            {
                bootstrapper = bootstrapperFactory.Invoke();

                bootstrapper.Run();
            }
            catch (Exception exception)
            {
                if (exceptionHandler == null)
                {
                    throw exception;
                }
                else
                {
                    exceptionHandler.Invoke(bootstrapper, exception);
                }
            }

            return bootstrapper;
        }
    }
}

